using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web;
using System.Web.Mvc;
using ExtendedMvc.Html;

namespace ExtendedMvc.Elements {
	public class Grid<TModel> : GridBase<Grid<TModel>, TModel> where TModel : class {
		public Grid(IModelViewHelper<IEnumerable<TModel>> helper, Expression<Func<TModel, object>> keySelector, object options = null, string id = null) : base(helper, helper.ViewDataContainer.Model, keySelector, options, id) { }
	}

	public abstract class GridBase<TGrid, TModel> : TableElement<TGrid>
		where TGrid : GridBase<TGrid, TModel>
		where TModel : class {
		private static object _gridNumKey;
		private Type _rowType;
		private IEnumerable<TModel> _source;
		private Dictionary<string, object> _options;
		private GridColumnBuilder<TModel> _columns;
		private Expression<Func<TModel, object>> _keySelector;

		protected IModelViewHelper<IEnumerable<TModel>> helper { get; private set; }
		protected ModelMetadata metadata { get; private set; }
		public new string Id { get { return id; } }

		static GridBase() {
			_gridNumKey = new object();
		}

		protected GridBase(IModelViewHelper<IEnumerable<TModel>> helper, IEnumerable<TModel> source, Expression<Func<TModel, object>> keySelector, object options = null, string id = null)
			: base(HtmlTableTag.Table, id) {
			this.helper = helper;
			_rowType = typeof(TModel);
			_source = source;
			metadata = helper.GetMetadataForType(_rowType);
			_options = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
			_keySelector = keySelector;
			if (options == null) return;
			foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(options))
				_options.Add(descriptor.Name, descriptor.GetValue(options));
		}

		public TGrid Columns(System.Action<GridColumnBuilder<TModel>> Builder) {
			_columns = new GridColumnBuilder<TModel>(helper, _keySelector);
			Builder(_columns);
			return (TGrid)this;
		}

		protected override TagRenderMode TagRenderMode { get { return TagRenderMode.Normal; } }

		protected override void SetId() {
			Id(GridIdGenerator());
			Builder.GenerateId(id);
		}

		protected override void SetContent() {
			Builder.InnerHtml = TableBuilder();
		}

		private string GridIdGenerator() {
			object count = helper.ViewContext.HttpContext.Items[_gridNumKey];
			int num = (count != null) ? ((int)count + 1) : 0;
			helper.ViewContext.HttpContext.Items[_gridNumKey] = num;
			return string.Format(CultureInfo.InvariantCulture, "grid{0}", new object[] { num });
		}

		private string TableBuilder() {
			if (_columns == null) {
				_columns = new GridColumnBuilder<TModel>(helper, _keySelector);
				_columns.ForCommands();
				var properties = _rowType.GetProperties();
				if (properties == null) return string.Empty;
				foreach (var property in properties) {
					var colMetadata = metadata.Properties.SingleOrDefault(p => p.PropertyName == property.Name);
					if (colMetadata == null || colMetadata.ShowForDisplay) _columns.ForData(property.Name);
				}
			}

			StringBuilder rows = new StringBuilder();
			rows.AppendLine(BuildHeaderRow());
			if (_source == null || !_source.Any()) {
				rows.AppendLine(BuildEmptyRow("no data", _columns.Count()));
			} else {
				for (var r = 0; r < _source.Count(); r++) {
					TModel item = _source.ElementAt(r);
					rows.AppendLine(BuildDataRow(item, r % 2 == 1));
				}
			}

			return rows.ToString();
		}

		private string BuildHeaderRow() {
			StringBuilder cells = new StringBuilder();
			if (_columns == null || !_columns.Any()) return string.Empty;
			foreach (var column in _columns) {
				TagBuilder cell = new TagBuilder(HtmlTableTag.TableHeaderCell);
				var value = column.GetHeader();
				if (value != null) cell.InnerHtml = value.ToString();
				cells.AppendLine(cell.ToString(TagRenderMode.Normal));
			}
			TagBuilder row = new TagBuilder(HtmlTableTag.TableRow);
			row.InnerHtml = cells.ToString();
			return row.ToString(TagRenderMode.Normal);
		}

		private string BuildDataRow(TModel item, bool isAlternate) {
			StringBuilder cells = new StringBuilder();
			if (_columns == null || !_columns.Any()) return string.Empty;
			foreach (var column in _columns) {
				TagBuilder cell = new TagBuilder(HtmlTableTag.TableCell);
				var value = column.GetData(item);
				if (value != null) cell.InnerHtml = value.ToString();
				cells.AppendLine(cell.ToString(TagRenderMode.Normal));
			}
			TagBuilder row = new TagBuilder(HtmlTableTag.TableRow);
			row.InnerHtml = cells.ToString();
			return row.ToString(TagRenderMode.Normal);
		}

		private string BuildEmptyRow(string message, int cols) {
			TagBuilder emptyCell = new TagBuilder(HtmlTableTag.TableCell);
			if (cols > 1) emptyCell.Attributes["colspan"] = cols.ToString();
			emptyCell.InnerHtml = HttpUtility.HtmlEncode(message);
			TagBuilder row = new TagBuilder(HtmlTableTag.TableRow);
			row.InnerHtml = emptyCell.ToString(TagRenderMode.Normal);
			return row.ToString(TagRenderMode.Normal);
		}
	}
}